package cn.xhh.dailyreview.server.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import cn.xhh.dailyreview.server.model.Category;
import cn.xhh.dailyreview.server.model.Review;
import cn.xhh.dailyreview.server.model.User;
import cn.xhh.dailyreview.server.utils.DbUtil;

/**
 * Data Access Object for {@link Review}
 * @author cooky
 *
 */
public class ReviewDAO {
	
	private static final Logger log = Logger.getLogger(CategoryDAO.class.getName());
	
	public boolean create(Review r) {
		if (r==null || r.invalid()) {
			log.severe("Creating/updating review failed - invalid: " + r);
			return false;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Creating/updating review...");
		
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			pm.makePersistent(r);
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINE))
			log.fine("Review created/updated. ID: " + r.getId() + ". Data: " + r);
		return true;
	}
	
	public Review read(Long id) {
		if (id == null) {
			log.severe("Reading review failed: id is null.");
			return null;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading review with id: " + id);
		
		Review r = null;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			r = pm.getObjectById(Review.class, id);
			r.getContent(); // load content explicitly
		} catch (JDOObjectNotFoundException e) {
			log.warning("Reading review failed: no review found with id: " + id);
			r = null;
		} finally {
			pm.close();
		}
		
		if (r!=null && log.isLoggable(Level.FINER))
			log.finer("Category read. ID: " + r.getId() + ". Data: " + r);
		return r;
	}
	
	@SuppressWarnings("unchecked")
	public List<Review> read(User u, Category c, int base, int max) {
		if (base<0 || max<0) {
			log.severe("Reading reviews failed: illegal arguments (base: " + base + ", max: " + max + ")");
			return null;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading reviews... base: " + base + ", max: " + max);
		
		List<Review> reviews = null;
		String filter = null;
		if (u!=null && u.getId()!=null)
			filter = "userId == " + u.getId();
		if (c!=null && c.getId()!=null)
			filter = ((filter == null) ? "" : (filter + " && ")) + "categoryId == " + c.getId();
		
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			Query q = pm.newQuery(Review.class);
			if (filter != null) q.setFilter(filter);
			q.setOrdering("userId asc, creationTime asc, categoryId asc");
			q.setRange(base, max==0 ? Long.MAX_VALUE : base+max);
			List<Review> got = (List<Review>) q.execute();
			reviews = new ArrayList<Review>(got.size());
			reviews.addAll(got);
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINER))
			log.finer("Reviews read: " + (reviews==null ? null : reviews.size()) 
					+ ". (base: " + base + ", max: " + max + ")");
		return reviews;
	}
	
	public int readCount(User u, Category c) {
		if (log.isLoggable(Level.FINEST))
			log.finest("Reading reviews count...");
		
		int count = -1;
		String filter = null;
		if (u!=null && u.getId()!=null)
			filter = "userId == " + u.getId();
		if (c!=null && c.getId()!=null)
			filter = ((filter == null) ? "" : (filter + " && ")) + "categoryId == " + c.getId();
		
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			Query q = pm.newQuery(Review.class);
			if (filter != null) q.setFilter(filter);
			q.setResult("count(this)");
			q.setUnique(true);
			count = (Integer) q.execute();
			q.closeAll();
		} finally {
			pm.close();
		}
		
		if (log.isLoggable(Level.FINER))
			log.finer("Reviews count read: " + count + ". filter: " + filter);
		return count;
	}
	
	public boolean update(Review r) {
		return create(r);
	}
	
	public boolean delete(Review r) {
		if (r==null || r.getId()==null) {
			log.severe("Deleting review failed: " + (r==null ? "review" : "ID") + " is null.");
			return false;
		}
		
		if (log.isLoggable(Level.FINEST))
			log.finest("Deleting review... ID: " + r.getId());
		
		boolean result = true;
		PersistenceManager pm = DbUtil.getPMF().getPersistenceManager();
		try {
			// first make sure the review is persistent and existent
			r = pm.getObjectById(Review.class, r.getId());
			pm.deletePersistent(r);
		} catch (JDOObjectNotFoundException e) {
			log.warning("Deleting review failed: no review found with ID: " + r.getId());
			result = false;
		} finally {
			pm.close();
		}

		if (result && log.isLoggable(Level.FINE))
			log.fine("Review deleted. ID: " + r.getId() + ". Data: [ " + r + " ]");
		return result;
	}

}
